//******************************************************************
//******************************************************************
//**********          ANts Peer To Peer Sources        *************
//
// ANts P2P realizes a third generation P2P net. It protects your
// privacy while you are connected and makes you not trackable, hiding
// your identity (ip) and crypting everything you are sending/receiving
// from others.

// Copyright (C) 2004  Roberto Rossi

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


package ants.p2p.utils.indexer;

import org.apache.log4j.*;
import java.util.*;
import ants.p2p.query.*;

public class SupernodeEngine extends Thread{
  BackgroundEngine caller;

  static Logger _logger = Logger.getLogger(SupernodeEngine.class.getName());

  public SupernodeEngine(BackgroundEngine caller){
    this.caller = caller;
    this.setPriority(1);
  }

  public void refreshSuperNodeList(){
    for(int x = this.caller.lastUsedSuperNodes.size()-1; x >= 0; x--){
      String supNode = (String)this.caller.lastUsedSuperNodes.get(x);
      if(!this.caller.wa.existRouteTo(supNode) || !this.caller.supernodeList.containsKey(supNode)){
        this.caller.lastTimeUploadedFileList.remove(supNode);
        this.caller.setLastUploadedList(supNode, null);
        this.caller.lastUsedSuperNodes.remove(supNode);
        this.caller.supernodeList.remove(supNode);
      }
    }
    Enumeration keys = this.caller.supernodeList.keys();
    while(keys.hasMoreElements()){
      String supNode = (String)keys.nextElement();
      if(!this.caller.wa.existRouteTo(supNode)){
        this.caller.lastTimeUploadedFileList.remove(supNode);
        this.caller.setLastUploadedList(supNode, null);
        this.caller.lastUsedSuperNodes.remove(supNode);
        this.caller.supernodeList.remove(supNode);
      }
    }
  }

  public void run(){
    try {
      sleep( (long) Math.floor(Math.random() * 1000 * 60 * 3));
    }
    catch (InterruptedException ex1) {
    }
    int counter = 0;
    while(!caller.terminate){
      try {
        _logger.info("Looking for supernodes...");
        if(this.caller.wa != null)
          this.caller.wa.doSupernodeQuery();
      }
      catch (Exception ex) {
        _logger.error("",ex);
      }
      try {
        sleep(120000);
        this.refreshSuperNodeList();
        if (BackgroundEngine.getInstance().isUploadingFileList()){
          _logger.info("Uploading file list...");
          continue;
        }
        for(int g = 0; g < this.caller.lastUsedSuperNodes.size() /*&& !this.caller.wa.isSupernode()*/; g++){
          long idleUploadingList = System.currentTimeMillis() - ((Long)this.caller.lastTimeUploadedFileList.get(this.caller.lastUsedSuperNodes.get(g))).longValue();
          if (idleUploadingList > BackgroundEngine.remoteIndexedDocumentsTimeout) {
            this.caller.setLastUploadedList((String)this.caller.lastUsedSuperNodes.get(g), null);
            QuerySupernodeTuple qst = (QuerySupernodeTuple)this.caller.supernodeList.get(this.caller.lastUsedSuperNodes.get(g));
            QueryFileListUploaderThread qflut = new QueryFileListUploaderThread(this.caller.wa, qst.getQueryMessage(), (String)this.caller.lastUsedSuperNodes.get(g));
            this.caller.lastTimeUploadedFileList.put(this.caller.lastUsedSuperNodes.get(g), new Long(System.currentTimeMillis()));
            BackgroundEngine.getInstance().uploadingFileList();
            qflut.start();
          }
          else if (this.caller.forceUploadingLists) {
            QuerySupernodeTuple qst = (QuerySupernodeTuple)this.caller.supernodeList.get(this.caller.lastUsedSuperNodes.get(g));
            QueryFileListUploaderThread qflut = new QueryFileListUploaderThread(this.caller.wa, qst.getQueryMessage(), (String)this.caller.lastUsedSuperNodes.get(g));
            this.caller.lastTimeUploadedFileList.put(this.caller.lastUsedSuperNodes.get(g), new Long(System.currentTimeMillis()));
            BackgroundEngine.getInstance().uploadingFileList();
            qflut.start();
          }
        }
        this.caller.forceUploadingLists = false;
        if (/*!this.caller.wa.isSupernode() &&*/
             this.caller.supernodeList.size() > 0 &&
             this.caller.lastUsedSuperNodes.size() < this.caller.uploadListToSupernodes &&
             this.caller.lastUsedSuperNodes.size() < this.caller.supernodeList.size()) {
          String dest;
          ArrayList supNodes = new ArrayList(this.caller.supernodeList.keySet());
          Collections.shuffle(supNodes);
          supNodes.removeAll(this.caller.lastUsedSuperNodes);
          dest = (String) supNodes.remove(0);
          while(supNodes.size() > 0 && this.caller.lastUsedSuperNodes.contains(dest)){
            dest = (String) supNodes.remove(0);
          }
          if(this.caller.lastUsedSuperNodes.contains(dest)) continue;
          this.caller.setLastUploadedList(dest, null);
          QuerySupernodeTuple qst = (QuerySupernodeTuple)this.caller.supernodeList.get(dest);
          QueryFileListUploaderThread qflut = new QueryFileListUploaderThread(this.caller.wa, qst.getQueryMessage(), dest);
          this.caller.lastUsedSuperNodes.add(dest);
          this.caller.lastTimeUploadedFileList.put(dest, new Long(System.currentTimeMillis()));
          BackgroundEngine.getInstance().uploadingFileList();
          qflut.start();
        }
      }
      catch (Exception ex2) {
        _logger.error("",ex2);
      }
    }
  }
}
